package com.luo.gmall.index.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

/**
 * @author Zoro
 * @date 2021年11月15
 */
@Configuration
public class DistributeLock {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private Timer timer;

    public Boolean lock(String lockName, String uuid, Integer expire) {
        // script 1.判断是否存在锁（exists ），不存在直接获取锁（hincrby），添加过期时间
        // 2.判断当前锁是否为自己的锁（hexists），如果为自己的锁，则重入（hincrby），添加过期时间
        // 3.否则获取锁失败，重新获取锁
        String script = "if redis.call('exists',KEYS[1]) == 0 or redis.call('hexists',KEYS[1],ARGV[1]) == 1 " +
                "then " +
                "   redis.call('hincrby',KEYS[1],ARGV[1],1) " +
                "   redis.call('expire',KEYS[1],ARGV[2]) " +
                "   return 1 " +
                "else " +
                "   return 0 " +
                "end";
        if (!Boolean.TRUE.equals(redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Collections.singletonList(lockName), uuid, expire.toString()))) {
            try {
                TimeUnit.MILLISECONDS.sleep(80);

                lock(lockName, uuid, expire);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 自动续期
        resetTime(lockName, uuid, 30);
        return true;
    }

    public void unLock(String lockName, String uuid) {
        // 1.判断是否为为自己的锁，否则认为恶意四释放锁
        // 2.如果存在自己的锁，则相对的（hincrby）-1 结果是否为0，为0的话释放锁（del），返回1
        // 3.减1后的值不为0，则返回0
        String script = "if redis.call('hexists',KEYS[1],ARGV[1]) == 0 " +
                "then " +
                "   return nil " +
                "elseif redis.call('hincrby',KEYS[1],ARGV[1],-1) == 0 " +
                "then " +
                "   return redis.call('del',KEYS[1]) " +
                "else " +
                "   return 0 " +
                "end";
        Long flag = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(lockName), uuid);
        if (flag == null) {
            throw new RuntimeException("你要关闭的锁不存在！");
        } else if (flag == 1) {
            timer.cancel();
        }
    }

    private void resetTime(String lockName, String uuid, Integer expire) {
        String script = "if redis.call('hexists',KEYS[1],ARGV[1]) == 1 " +
                "then " +
                "   return redis.call('expire',KEYS[1],ARGV[2]) " +
                "else " +
                "   return 0 " +
                "end";
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Collections.singletonList(lockName), uuid, expire.toString());
            }
        }, expire * 1000 / 3, expire * 1000 / 3);
    }


}
